今天的課題中,我們將來了解 video
物件,並透過其相關方法,來客製化一個影片播放器。[1]
實作範例
在HTML5推出之前,我們只能利用插件在網頁中嵌入影片,當HTML5推出之後, <video>
元素就變成在網頁中嵌入影片的標準方式。以下將先簡介幾個常用的方法以及屬性[2]
play()
:開始播放。pause()
:暫停播放。load()
:重新讀取。paused
:目前狀態為暫停True
或是播放false
。volume
:設定音量或回傳目前的音量。duration
:回傳Audio/Video元素的整體長度(秒)。currentTime
:設定播放的時間點或回傳目前播放的時間點。知道 <video>
元素的相關方法與屬性之後,就可以開始設定各個元素的監聽事件與函式,為我們的播放器加上相關功能。
首先我們先將會用到的元素選取起來:
var selectVideo = document.querySelector('video');
var playButton = document.querySelector('.toggle');
var inputItem = document.querySelectorAll('input');
var skipButton = document.querySelectorAll('.player__button[data-skip]');
var progressBarOut = document.querySelector('.progress');
var progressBarIn = document.querySelector('.progress__filled');
當點擊影片或播放鈕時,我們想要使影片播放或暫停,並同時將播放鈕改為暫停或播放的圖示。
我們可以將指定元素加上監聽事件 click
來觸發函數,並透過<video>
元素的 play()
或 pause()
方法來播放或暫停,且透過更改 .toggle
元素的 innerHTML
來更動播放鈕的圖示:
//選取影片及按鈕元素並加上監聽事件與執行函示
playButton.addEventListener('click', playToggle);
selectVideo.addEventListener('click', playToggle);
function playToggle() {
//當影片狀態為暫停的時候
if (selectVideo.paused) {
//播放影片
selectVideo.play();
//將播放鈕圖示改為暫停鈕圖示
playButton.innerHTML = '❚ ❚';
//當影片是播放的時候
} else {
//暫停影片
selectVideo.pause();
//將暫停鈕圖示改為播放鈕圖示
playButton.innerHTML = '►';
};
};
下一步我們要將音量控制器與速度控制器與影片屬性連接。
一樣也是先將指定的元素加上監聽事件,並透過函式內容來修改影片的屬性,最後完成改變影片的音量或播放速度。
作者在 input
元素中有加入 name
屬性,且該屬性值分別就是影片屬性的名稱,因此我們可以利用 event.target.name
來取得元素中 name
屬性值,並將其指定成變數,讓函式可以自行判斷要更改的屬性,如此一來就可以利用同一個函式來更改音量或播放速度:
//利用forEach()方法將選到的每個input元素加上監聽事件
inputItem.forEach(function(item){
item.addEventListener('input', changeCondition);
});
function changeCondition (event) {
//取得觸發事件的input元素
let changeInput = event.target;
//取得input元素的name屬性
let conditionName = changeInput.name;
//取得input元素的值
let conditionValue = changeInput.value;
//將影片屬性值改為input元素的值
selectVideo[conditionName] = conditionValue;
};
將快轉或倒帶的元素加上監聽事件後,在執行函數內透過取得指定元素的 data-attribute
的屬性值,再將目前的影片時間加上我們取得的值,再指派給 currentTime
,我們就能在播放器上做出有快轉或是倒帶功能的按鈕:
function skipVideo(event) {
let changeTime = parseInt(event.target.dataset.skip);
selectVideo.currentTime += changeTime;
};
最後我們要替播放器加上時間軸,並且可以透過點擊時間軸上的位置,將播放時間指定到我們選定的位置。
目前在我們的時間軸上,有作為背景的 .progress
元素,以及用來顯示目前進度的 .progress__filled
元素。
timeupdate
。timeupdate
事件每當指定元素的播放時間改變就會被觸發,在觸發的函式中結合影片的 duration
以及 currentTime
屬性,來換算出目前的播放時間比例,最後再把這個值指定為時間軸元素的寬度,就可以讓時間軸隨著影片播放時間增加跟著變長://將影片加上監聽事件以及觸發函示
selectVideo.addEventListener('timeupdate', progressing);
function progressing() {
//取得影片時間總長度
let videoDuration = selectVideo.duration;
//取得影片目前時間長度
let currentPosition = selectVideo.currentTime;
//換算成比例
currentProgress = currentPosition / videoDuration * 100;
//將算出來的比例加到該元素的CSS屬性上
progressBarIn.style.flexBasis = `${currentProgress}%`;
};
.progress
元素。.progress
元素,被觸發監聽事件並執行函示,在觸發的函式中透過 event.offsetX
方法,取得目前滑鼠在該元素上的X軸座標,並且透過 offsetWidth
屬性,取得該元素的整體長度,最後再換將這兩個值換算出來的比例,換算成目前的播放時間指定給影片,如此一來我們就可以完成我們的時間軸功能://當滑鼠被按下時,執行addDragProgress函式
progressBarOut.addEventListener('mousedown', addDragProgress);
//當滑鼠被放開時,執行removeDragProgress函式
progressBarOut.addEventListener('mouseup', removeDragProgress);
function addDragProgress (e) {
//當函示被執行時,新增監聽mousedown以及mousemove事件
progressBarOut.addEventListener('mousedown', dragProgressBar);
progressBarOut.addEventListener('mousemove', dragProgressBar);
};
function removeDragProgress () {
//當函示被執行時,移除監聽mousemove事件
progressBarOut.removeEventListener('mousemove', dragProgressBar);
};
function dragProgressBar (e) {
//取得影片總長度
let videoDuration = selectVideo.duration;
//取得按下按鍵時的滑鼠在該元素的X軸座標
let mouseX = e.offsetX;
//取得時間軸背景元素總長度
let progressBarWidth = progressBarOut.offsetWidth;
//換算成長度比例
let videoProgress = mouseX / progressBarWidth;
//換算成影片時間
let newPosition = videoDuration * videoProgress;
//將計算出來的影片時間指定為目前播放時間
selectVideo.currentTime = newPosition;
};
在後面的函式中我們並沒有將換算出來的長度比例指定給時間軸,這是因為透過前面繪製時間軸的監聽事件 timeupdate
,在改變播放時間的那一瞬間,該函式就被處發並完成函式內容,將時間換算成時間軸長度。
另外可以在函式 addDragProgress()
中,我們又額外監聽了一次 mousedown
事件,這是因為如果只監聽 mousemove
事件的話,當我們在時間軸上按下滑鼠之後,需要再移動滑鼠才能觸發時間轉換的函式。但一般的播放器當我們點擊時間軸上的任意位置,會直接跳到對應的播放時間,因此在這邊多加上監聽 mousedown
事件,就可以做出點擊時間軸就馬上跳到對應的影片位置。
另外我們有將幾個點擊功能與鍵盤按鈕綁定,嘗試做出幾個 youtube 網站上的播放器功能,大家可以在實作連結中試看看,code內容如下:
window.addEventListener('keydown', keyboardFunction);
function keyboardFunction(e) {
e.preventDefault();
if (e.keyCode === 37) {
let backButton = document.querySelector('.player__button[data-skip="-10"]');
console.log(backButton);
backButton.click();
};
if (e.keyCode === 39) {
let nextBotton = document.querySelector('.player__button[data-skip="25"]');
console.log(nextBotton);
nextBotton.click();
};
if (e.keyCode === 38 && selectVideo.volume <= 1) {
let volumeSlide = document.querySelector('[name = "volume"]');
volumeSlide.stepUp();
selectVideo.volume = volumeSlide.value;
console.log(volumeSlide.value);
};
if (e.keyCode === 40 && selectVideo.volume > 0) {
let volumeSlide = document.querySelector('[name = "volume"]');
volumeSlide.stepDown();
selectVideo.volume = volumeSlide.value;
console.log(volumeSlide.value);
};
if (e.keyCode === 32) {
playToggle();
};
};
在今天的播放器課題當中,我們學到以下的技能:
透過事件的觸發,在執行函示中使用 video
物件的方法或是更改屬性,我們就可以創造出一個充滿各種神奇功能的播放器。以上是今天的方法與心得,感謝您的閱讀。
你好,我最近剛好也在進行js30,常常翻閱你們的教學~
想請問第二點的
selectVideo[conditionName] = conditionValue;
這邊是基於甚麼條件或特性必須使用中括號呢??
(為什麼selectVideo.conditionName會出錯呢)
我好像自己找到答案了
用 點號 .
存取時,點號右邊必須是一個可識別字(identifier),例如屬性名稱,不能是字串、變數、運算式...等。但用 中括號 []
存取時,中括號裡面可以放字串、變數、運算式等。
應該是這樣沒錯吧...XD
想到竟然還有人在看我的文章大驚!
其實你已經答對了XD
物件在改變屬性的時候有兩種方法
objectName.propertyName
objectName["propertyName"]
所以今天當你是用一個變數來對物件進行操作時,使用第一種方法會因為找不到屬性而失敗,使用第二種方法才會成功。
舉上面的例子來說,當我們改變 volume 這個屬性的時候:
第一種方法會因在我的 selectVideo 裡面沒有 "conditionName" 這個屬性,所以反而變成在 selectVideo 增加一個 key 為 conditionName 的屬性。
第二種方法會先把 selectVideo[conditionName]
轉換成 selectVideo["volume"]
然後就可以成功取代成新值。
希望有幫助到你!